#include "ohci.h"
#include"memrw.h"
#include<dos.h>
#ifdef DEBUG
#include<stdio.h>
#endif
void buildOHCITD(BYTE PID,BYTE DI,BYTE DataTggle,DWORD CBP,DWORD length,OHCITDType * pNextTD,OHCITDType * pTD);
void buildOHCIED(BYTE FA,BYTE EN,BYTE Direction,BYTE speed,WORD maxPackSize,OHCITDType * TailP,OHCITDType * HeadP,EDType * pED);
void buildHccaStructure(OHCIDescriptorType * pDT);
void resetPort (HCStrucType * HCptr, int portNumber);

ControllerAccessMethodType OHCIAccessMethod={&OHCI_ControllerInitial,\
											&OHCI_ControlTransfer,\
	                                                                      &OHCI_bulkTransfer,\
	                                                                      &OHCI_interruptTransfer,\
	                                                                      &OHCI_pointToDevBuffer,\
	                                                                      &OHCI_DevPortInit};

/******************************************************************************
    Function:
        OHCI_ControllerInitial

    Description:
	initialize OHCI controller.

    Entry:
	pHC   pointer to controller description

    Exit:
        none
******************************************************************************/
void OHCI_ControllerInitial(void * pHC)
{

	  BYTE i;
         //2 get host controller info
         HCStrucType * CPtr=(HCStrucType *)pHC;
         CPtr->bHCType = OHCITYPE;
	  CPtr->bNumPorts = getMemDWORD(CPtr->dBaseAddress + HcRhDescriptorA) & NumberDownstreamPorts;
//	  CPtr->dHcdDataArea= readPortDWORD(CPtr->dBaseAddress + FLBASEADD ) & 0xfffff000;
	  CPtr->pDescriptorPtr=allocAlignMem(sizeof(OHCIDescriptorType) + 256, 256);
	  OHCIDescriptorType * pDT=(OHCIDescriptorType *)CPtr->pDescriptorPtr;
	  cleanBuffer((BYTE *) pDT, sizeof(OHCIDescriptorType));	  
	  DWORD dwBaseMMIO = CPtr->dBaseAddress;


	 //2.USB controller goto the USBOPERATIONAL state.
	 setMemDWORD(dwBaseMMIO+ HcControl, 0xffffff3f, 0x80);	 

	 //port power on
	 if(getMemDWORD(dwBaseMMIO+ HcRhDescriptorA) & PowerSwitchingMode)
	 {
	 	//per-port power
	 	 for( i =0; i< CPtr->bNumPorts; i++)
	 	 {
	 	 	setMemDWORD(dwBaseMMIO + HcRhPort1Status + i *4, -1, SetPortPower);
	 	 }
	 }
	 else
	 { 	//all-port power
	 	setMemDWORD(dwBaseMMIO + HcRhStatus, 0xfffeffff, SetGlobalPower);
	 }
	 
	  //2. config interrupt and Isochronous ED
	  
	  //hcca
	  buildHccaStructure(pDT);
//	  dumpDWORD(pDT->HCCA.InterruptTable, 32);//test
//	  printf("ED32 address %lx \n", calcLineAddress((BYTE *)&pDT->ED32ms));//test
//	  dumpDWORD((DWORD *)&pDT->ED32ms, 6 * 4);//TEST
	  writeMemDWORD(dwBaseMMIO + HcHCCA, calcLineAddress((BYTE *)&pDT->HCCA));	 
	  setMemDWORD(dwBaseMMIO + HcControl, ~PeriodicListEnable, PeriodicListEnable);

	  //2. config control ED
	  writeMemDWORD(dwBaseMMIO + HcControlHeadED, calcLineAddress((BYTE *)&pDT->ControlED));
	  setMemDWORD(dwBaseMMIO + HcControl, ~ControlListEnable, ControlListEnable);

	  //2. config bulk ED
	  writeMemDWORD(dwBaseMMIO + HcBulkHeadED,calcLineAddress((BYTE *)&pDT->BulkED));
	  setMemDWORD(dwBaseMMIO + HcControl, ~BulkListEnable, BulkListEnable);

	  //enable interrupt
	  writeMemDWORD(dwBaseMMIO + HcInterruptStatus, getMemDWORD(dwBaseMMIO + HcInterruptStatus) | 0x4000003f);
	  writeMemDWORD(dwBaseMMIO + HcInterruptEnable, WriteBackDoneHead + BIT6 + BIT30 + BIT31);
	
	  //2.init device
	  for( i =0; i< CPtr->bNumPorts; i++)
	  {		
	  	if(getMemDWORD(dwBaseMMIO + HcRhPort1Status + i *4) & CurrentConnectStatus) //device connected
	  	{
	  		resetPort(CPtr,i);
                     devInitialize(CPtr,i); //  init here.       			
			
	  	}
	  }
}






/******************************************************************************
    Function:
        OHCI_ControlTransfer

    Description:
	OHCI control tansfer type

    Entry:
	ptr --  device description
	RFPtr -- request data
    Exit:
        status    Condition code
    Note: 
          if want to transmit data , please place it in UHCIDescriptor.buffer1;
          receve data after transation also put it in UHCIDescriptor.buffer1;
******************************************************************************/
BYTE  OHCI_ControlTransfer(DeviceInfoPtr ptr,  RequstFormatType * RFPtr)
{
	OHCIDescriptorType * pDT= (OHCIDescriptorType *)ptr->pHCStrucPtr->pDescriptorPtr;
	DWORD dwHCMMIO = ptr->pHCStrucPtr->dBaseAddress;
	BYTE address=ptr->bDeviceAddress;
	BYTE endpoint=0;
	WORD maxPacketSize= ptr->wEndp0MaxPacket;
	BYTE speed=0;
	WORD length= RFPtr->wLength;
	

	//2.build SETUP TD
	OHCITDType * pTD=&pDT->ControlSetup;
	OHCITDType * pNextTD;
	if(length)pNextTD= &pDT->ControlData;
	else pNextTD= &pDT->ControlStatus;
	
	cleanBuffer((BYTE *)pTD, sizeof(OHCITDType));
	buildOHCITD(PIDSETUP, NOINTR, Data0Tg, calcLineAddress((BYTE *) RFPtr), sizeof(RequstFormatType), \
		pNextTD,  pTD);

	//2.build DATA TD
	BYTE direction= RFPtr->bmRequestType  & 0x80;//  1-device-to-host;
	BYTE pid;
	if(length)
	{
		pTD= &pDT->ControlData;
		cleanBuffer((BYTE *)pTD, sizeof(OHCITDType));	
		if(direction)pid= PIDIN; // IN endpoint
		else pid= PIDOUT; // OUT endpoint
		buildOHCITD(pid, NOINTR, ToggleCarry, calcLineAddress( pDT->buffer), RFPtr->wLength , \
			&pDT->ControlStatus, pTD);	
	}

	//2.build Status TD
	pTD= &pDT->ControlStatus;
	cleanBuffer((BYTE *)pTD, sizeof(OHCITDType));	
	if(direction)pid= PIDOUT; // OUT endpoint
	else pid= PIDIN; //IN endpoint
	buildOHCITD(pid, NOINTR, Data1Tg, 0, 0, &pDT->NullTD,  pTD);

	//2.Clean null TD
	pTD= &pDT->NullTD;
	cleanBuffer((BYTE *)pTD, sizeof(OHCITDType));	

	//2.build ED
	EDType * pED= &pDT->ControlED;
	cleanBuffer((BYTE *)pED, sizeof(EDType));
	if(ptr->bEndpointSpeed == Low_Speed_Device)speed =1; 
	buildOHCIED(address, endpoint, 0, speed, maxPacketSize, &pDT->NullTD, &pDT->ControlSetup,  pED);
	
#ifdef DEBUG
	printf("ED TD   before transation:\n");
	dumpDWORD((DWORD * )&pDT->ControlSetup, 20);
#endif	
	
	//2.start transfer
	setMemDWORD(dwHCMMIO + HcCommandStatus,~ ControlListFilled, ControlListFilled);

	//2.wait to complete.	
	WORD i=0;
	DWORD dwTemp=calcLineAddress((BYTE *)&pDT->ControlStatus);
	for(i=0;i<TransationTime;i++)
	{
		if((getMemDWORD(dwHCMMIO + HcInterruptStatus) & WriteBackDoneHead))
		{
				writeMemDWORD(dwHCMMIO + HcInterruptStatus, WriteBackDoneHead);	
				if(pDT->HCCA.dwDoneHead ==dwTemp)break;
		}
		else delay(1);
	}
	if(i >  TransationTime)
	{
#ifdef DEBUG			
			printf("control TD not complete. \n");
#endif	
	}
	
#ifdef DEBUG
	printf("Controlsetup   after transation:\n");
	dumpDWORD((DWORD * )&pDT->ControlSetup, 36);
	printf("\n");
#endif
           
  	return pDT->ControlStatus.Control >> 28;		 
}




/******************************************************************************
    Function:
        OHCI_bulkTransfer

    Description:
	OHCI bulk tansfer type

    Entry:
	DIPtr --  device description
	direction -- data transfer direction
       bufferPtr: data buffer to send or received
       length-- data length
       ToggleValue--refer to usb2.0 8.5.2. For bulk read, buld write, data begins with 0, alter each success transfer.
       TransLength-- actual transfer length, byte unit.
	
    Exit:
        status    0 ----ok.
                    bit0: STALL
                    bit1: time out
                    bit2: error
NOTE: this function need to further verify.                     

******************************************************************************/
BYTE OHCI_bulkTransfer(DeviceInfoPtr DIPtr, BYTE direction, BYTE * bufferPtr,WORD length,WORD *TransLength)
{
	   BYTE  EndPoint;
	   WORD MaxLen;
	   BYTE  USBAddress;
	   BYTE  Toggle;
          BYTE  status=0;
	   BYTE  directionPID=0;
	   BYTE  temp=0;
	   DWORD dwMMIObase=DIPtr->pHCStrucPtr->dBaseAddress;	   
		  
	   if(direction)
	   { 
		   EndPoint = DIPtr->bBulkInEndpoint;
		   MaxLen=DIPtr->wBulkInMaxPkt;
		   Toggle=DIPtr->bBulkInToggle;
		   directionPID = 2; //
	   }
	   else
	   {
		   EndPoint = DIPtr->bBulkOutEndpoint;
		   MaxLen=DIPtr->wBulkOutMaxPkt;	   
		   Toggle=DIPtr->bBulkOutToggle;
		   directionPID = 1;		   
	   }

	   Toggle |= BIT1; // set MSB bit , used toggle bit in TD
	   
	   USBAddress=DIPtr->bDeviceAddress;	  
	   BYTE speed=0;
	   if(DIPtr->bEndpointSpeed == Low_Speed_Device) speed =1;
	   OHCIDescriptorType * pDT= (OHCIDescriptorType *)DIPtr->pHCStrucPtr->pDescriptorPtr;	   

	 //2 set skip bit
	 pDT->BulkED.Control =SKIP_ED;
	  //2build TD
	   buildOHCITD(2, 0, Toggle, calcLineAddress(bufferPtr), length, 0, &pDT->BulkData);
	  //2build ED
	   buildOHCIED(USBAddress, EndPoint, directionPID, speed, MaxLen, 0, &pDT->BulkData, &pDT->BulkED);

	  DWORD dwMMIOBase= DIPtr ->pHCStrucPtr->dBaseAddress;
#ifdef DEBUG
	printf("bulk  before transation:\n");
	printf("bulk head %lx \n",getMemDWORD(dwMMIOBase + HcBulkHeadED));
	printf("bulk current ED %lx \n",getMemDWORD(dwMMIOBase + HcBulkCurrentED));	
	dumpDWORD((DWORD * )&pDT->BulkData, 8);	
#endif	

	   //2start transfer	
	   setMemDWORD(dwMMIObase + HcCommandStatus , ~BulkListFilled, BulkListFilled);

	   //2wait to complete.
	   BYTE i=0;

	while(!(getMemDWORD(dwMMIObase + HcInterruptStatus) & WriteBackDoneHead))
	{
		delay(1);
		i++;
		if(i > TransationTime)
		{
#ifdef DEBUG			
			printf("bulk TD not complete. \n");
#endif
			break;
		}
		
	}
	writeMemDWORD(dwMMIObase + HcInterruptStatus, WriteBackDoneHead);
	
#ifdef DEBUG
	printf("bulk  after  transation:\n");
	dumpDWORD((DWORD * )&pDT->BulkData, 8);
	printf("bulk head %lx \n",getMemDWORD(dwMMIOBase + HcBulkHeadED));
	printf("bulk current ED %lx \n",getMemDWORD(dwMMIOBase + HcBulkCurrentED));	
	printf("done TD %lx \n",getMemDWORD(dwMMIOBase + HcDoneHead));		

	printf("\n");
#endif		  
	 // set skip bit
	 pDT->BulkED.Control =SKIP_ED;
	  //data toggle
	  //better way: we use HCCA done head to get latest TD to improve software effective.
	  Toggle = pDT->BulkED.HeadP;
	  Toggle &= 0x02;
	  Toggle >>=1;
	  if(direction) DIPtr->bBulkInToggle = Toggle;
	  else 		 DIPtr->bBulkOutToggle=Toggle;

	  if(pDT->BulkData.CurrentBufferP ==0) *TransLength = length;//transfer complete
	  else *TransLength = pDT->BulkData.CurrentBufferP - calcLineAddress(bufferPtr);

	  temp = (pDT->BulkData.Control) >> 28;
	  if(temp)
	  {
	  	if(temp == 4) status =1;
		else if(temp == 5) status =2;
		else status =4;
	  }
	  
        return status;
}




/******************************************************************************
    Function:
        OHCI_interruptTransfer

    Description:
	OHCI bulk tansfer type

    Entry:
	DIPtr --  device description
	direction -- data transfer direction
       bufferPtr: data buffer to send or received
       length-- data length
       TransLength-- actual transfer length, byte unit.
	
    Exit:


******************************************************************************/
BYTE OHCI_interruptTransfer(DeviceInfoPtr DIPtr, BYTE direction, BYTE * bufferPtr,WORD length,WORD *TransLength)
{
	BYTE status=0;

	OHCITDType * pTD= (OHCITDType * )DIPtr->ptrPollTD;
	EDType	      * pED=(EDType	      *)DIPtr->ptrPollED;
	
	BYTE endpoint= DIPtr->bIntEndpoint;
	BYTE address= DIPtr->bDeviceAddress;
	WORD maxPacket=DIPtr->wIntMaxPkt;
	BYTE speed=0;
	BYTE pid;
	BYTE toggle;
	if(direction)pid= PIDIN;
	else pid= PIDOUT;
	
	if(DIPtr->bEndpointSpeed == Low_Speed_Device)speed =1; 	

	toggle= DIPtr->bIntEndPointToggle;
	toggle |= BIT1;
	
	//2.build TD,ED
	pED->Control = SKIP_ED;
	
	buildOHCITD(0, 0, toggle, calcLineAddress(bufferPtr), length, 0, pTD);

	buildOHCIED(address, endpoint, pid, speed, maxPacket, 0, pTD, pED);

	//2.wait to complete.
	DWORD dwBase= DIPtr->pHCStrucPtr->dBaseAddress;
	BYTE i=0;
	while(!(getMemDWORD(dwBase + HcInterruptStatus) & WriteBackDoneHead))
	{
		delay(1);
		i++;
		if(i > TransationTime)
		{
			printf("interrupt TD not complete. \n");
			break;
		}
		
	}
	writeMemDWORD(dwBase + HcInterruptStatus, WriteBackDoneHead);
	toggle =(pED->HeadP) & BIT1;
	DIPtr->bIntEndPointToggle = toggle >>1;

	  if(pTD->CurrentBufferP ==0) *TransLength = length;//transfer complete
	  else *TransLength = pTD->CurrentBufferP - calcLineAddress(bufferPtr);
	
	return status;
}

/******************************************************************************
    Function:
        OHCI_DevPortInit

    Description:
	OHCI port init.

    Entry:
	device info pointer

    Exit:
       none
******************************************************************************/
void OHCI_DevPortInit(DeviceInfoPtr ptr,BYTE portIndex)
{
	if(getMemDWORD(ptr->pHCStrucPtr->dBaseAddress + HcRhPort1Status + portIndex *4) & LowSpeedDeviceAttached)
			ptr->bEndpointSpeed=Low_Speed_Device ; // 01 =low speed device.	
      	else  	ptr->bEndpointSpeed=Full_Speed_Device; // 2 = full speed		

	ptr->ptrPollED = allocAlignMem(sizeof(EDType) + 16,16);
	ptr->ptrPollTD = allocAlignMem(sizeof(OHCITDType) + 16,16);

	//insert poll ED into 16ms ED tree -----temp !!! it should compare to bInterval.
	// should move to late init?????
	EDType * pED1;
	EDType * pED3=(EDType * )ptr->ptrPollED;		
	OHCIDescriptorType * pDT=(OHCIDescriptorType *)ptr->pHCStrucPtr->pDescriptorPtr;
	pED1= &pDT->ED16ms;

	pED3->Control = BIT14;//SKIP
	pED3->NextED = pED1->NextED;

	pED1->NextED= calcLineAddress((BYTE *)pED3);
	
	
}



/******************************************************************************
    Function:
        OHCI_pointToDevBuffer

    Description:
	OHCI device data buffer pointer. Control transation  data buffer.

    Entry:
	device info pointer

    Exit:
       OHCI device data buffer pointer
******************************************************************************/
BYTE * OHCI_pointToDevBuffer(DeviceInfoPtr ptr)
{
		return	((OHCIDescriptorType *)ptr->pHCStrucPtr->pDescriptorPtr)->buffer;
}


/******************************************************************************
    Function:
        getUHCIMethod

    Description:
	return OHCI interface.

    Entry:
	none

    Exit:
        return OHCI method interface 
******************************************************************************/
ControllerAccessMethodType * getOHCIMethod()
{
	return &OHCIAccessMethod;
}



/******************************************************************************
    Function:
        buildOHCITD

    Description:
	create ohci TD structure.

    Entry:
	PID -- 
	DI --DelayInterrupt , 7 - no interrupt
	DataTggle -- MSB 0: use toggleCarry in ED
			 -- MSB 1: use LSB	
	CBP -- CurrentBufferPointer		 
	length -- length of data to transfer.
	pNextTD -- pointer to NEXT TD address
	pTD -- pointer to TD
	
    Exit:
        NONE
******************************************************************************/
void buildOHCITD(BYTE PID,BYTE DI,BYTE DataTggle,DWORD CBP,DWORD length,OHCITDType * pNextTD,OHCITDType * pTD)
{
	DWORD temp=0;
	temp =0x0f;//NOT ACCESSED
	temp <<=4;
	temp += DataTggle;
	temp <<= 3;
	temp += DI;
	temp <<= 2;
	temp += PID;
	temp <<= 1;
	temp += 1;//bufferRounding always 1
	temp <<= 18;

	pTD->Control = temp;
	if(pNextTD)	pTD->NextTD = calcLineAddress((BYTE *)pNextTD );
	else		pTD->NextTD =0;

	pTD->CurrentBufferP= CBP;
	pTD->BufferEnd = CBP + length -1;

	return;	
}


/******************************************************************************
    Function:
        buildOHCIED

    Description:
	create ohci ED structure.

    Entry:
	FA -- function address
	EN -- endpoint number
	Direction -- direction of data flow
	speed -- 0:full speed       1:low speed
	maxPackSize -- max packet size
	TailP -- pointer to tail TD
	HeadP --pointer to head TD
	pED -- pointer to ED
	
    Exit:
       NONE

       note:assume NO next ED.
******************************************************************************/
void buildOHCIED(BYTE FA,BYTE EN,BYTE Direction,BYTE speed,WORD maxPackSize,OHCITDType * TailP,OHCITDType * HeadP,EDType * pED)
{
	DWORD temp=0;
	temp = maxPackSize;
	temp <<= 3;
	temp += speed;
	temp <<= 2;
	temp += Direction;
	temp <<=4;
	temp += EN;
	temp <<=7;
	temp += FA;

	if(TailP)	pED->TailP= calcLineAddress((BYTE *)TailP);
	else	 pED->TailP =0;
	
	pED->HeadP = calcLineAddress((BYTE *)HeadP);
	pED->NextED =0;
	pED ->Control= temp;	
}


/******************************************************************************
    Function:
        resetPort

    Description:
	reset OHCI port.

    Entry:
	none

    Exit:
        none
******************************************************************************/
void resetPort (HCStrucType * HCptr, int portNumber)
{
	DWORD address = HCptr->dBaseAddress + HcRhPort1Status + portNumber *4;
	writeMemDWORD(address, SetPortReset);
	delay(100);

}


/******************************************************************************
    Function:
        buildHccaStructure

    Description:
	build hcca structure.

    Entry:
	none

    Exit:
        none
note: do not change the sequence of build list.        
******************************************************************************/
void buildHccaStructure(OHCIDescriptorType * pDT)
{
	DWORD *ptr= pDT->HCCA.InterruptTable;
	EDType * ptrED;
	DWORD dwAddress, dwTemp,dwTemp1;	
	BYTE i;
	
	//build 32 ms
	ptrED = &pDT->ED32ms;
	ptrED->Control = BIT14; //SET SKIP
	ptr[0]= calcLineAddress((BYTE *)ptrED);

	//build 16 ms
	ptrED= &pDT->ED16ms;
	ptrED->Control = BIT14; //SET SKIP
	dwAddress= calcLineAddress((BYTE *)ptrED);
	ptr[1]=dwAddress;
	ptr[1 + 16]=dwAddress;

	//build 8ms
	ptrED= &pDT->ED8ms;
	ptrED->Control = BIT14; //SET SKIP	
	dwAddress= calcLineAddress((BYTE *)ptrED);
	for(i= 2; i<32; i= i+8)
	{
		ptr[i]= dwAddress;
	}

	//build 4ms
	ptrED= &pDT->ED4ms;
	ptrED->Control = BIT14; //SET SKIP	
	dwAddress= calcLineAddress((BYTE *)ptrED);
	for(i= 3; i<32; i= i+4)
	{
		ptr[i]= dwAddress;
	}
	
	//build 2ms
	ptrED= &pDT->ED2ms;
	ptrED->Control = BIT14; //SET SKIP	
	dwAddress= calcLineAddress((BYTE *)ptrED);
	for(i= 0; i<32; i= i+2)
	{
		if(ptr[i] == 0)	ptr[i]= dwAddress;
		else
		{
			dwTemp = ptr[i];
			//get last ED
			dwTemp1= getMemDWORD(dwTemp + 12);
			
			while(dwTemp1)
			{
//				printf("index: %d current ED address %lx   next ED address %lx    \n", i,dwTemp,dwTemp1);
				dwTemp = dwTemp1;
				dwTemp1 = getMemDWORD(dwTemp + 12);

			}
			if(dwTemp != dwAddress)//if found different ED.
			{
				writeMemDWORD(dwTemp + 12, dwAddress);
			}
		}
	}	

	//build 1ms
	ptrED= &pDT->ED1ms;
	ptrED->Control = BIT14; //SET SKIP	
	dwAddress= calcLineAddress((BYTE *)ptrED);
	for(i= 0; i<32; i++)
	{
		if(ptr[i] == 0)	ptr[i]= dwAddress;
		else
		{
			dwTemp = ptr[i];
			//get last ED
			dwTemp1= getMemDWORD(dwTemp + 12);
			
			while(dwTemp1)
			{
//				printf("index: %d current ED address %lx   next ED address %lx    \n", i,dwTemp,dwTemp1);
				dwTemp = dwTemp1;
				dwTemp1 = getMemDWORD(dwTemp + 12);

			}
			if(dwTemp != dwAddress)//if found different ED.
			{
				writeMemDWORD(dwTemp + 12, dwAddress);
			}
		}
	}		
	
	return;
}


